/*
 * Copyright (c) 2022 Kirky-X
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.lingeringdream.luciole.helper;

import com.alibaba.fastjson.JSONObject;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import okhttp3.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * http请求
 *
 * @author Kirky
 * @version 1.0
 * @date 2022/4/3
 */
public class HttpHelper {
    private static final HiLogLabel TAG = new HiLogLabel(HiLog.LOG_APP, 0XD0013, HttpHelper.class.getSimpleName());
    private static TaskDispatcher dispatcher;
    private final OkHttpClient client;

    private HttpHelper() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
//                连接超时
                .connectTimeout(15, TimeUnit.SECONDS)
//                写入超时
                .writeTimeout(20, TimeUnit.SECONDS)
//                读取超时
                .readTimeout(20, TimeUnit.SECONDS);
        client = builder.build();
        HiLog.info(TAG, "HttpHelper init.");
    }

    public static HttpHelper getInstance() {
        return Holder.INSTANCE;
    }

    public static HttpHelper getInstance(TaskDispatcher dispatcher) {
        if (HttpHelper.dispatcher == null && dispatcher != null) {
            HiLog.info(TAG, "dispatcher is null, set dispatcher.");
            HttpHelper.dispatcher = dispatcher;
        }
        return Holder.INSTANCE;
    }

    /**
     * 同步发起网络请求
     *
     * @param request 请求体
     * @return 请求结果，发生exception时返回null
     */
    public Map<String,Object> synchronousExecute(final Request request) {
        String url = request.url().url().toString();
        HiLog.info(TAG, "Synchronize requests to URL: " + url);
        try (Response response = client.newCall(request).execute()) {
            Map<String,Object> res = new HashMap<>(3);
            res.put("code",response.code());
            res.put("message",response.message());
            res.put("body",response.body().string());
            return res;
        } catch (IOException e) {
            String errorMessage = String.format("Url: %s\tError: %s", request.url().url(), e.getLocalizedMessage());
            HiLog.error(TAG, errorMessage);
        }
        return null;
    }

    /**
     * 异步发起网络请求
     *
     * @param request        请求体
     * @param resultCallback 回调
     */
    public void asynchronousExecute(final Request request, final ResultCallback resultCallback) {
        String url = request.url().url().toString();
        HiLog.info(TAG, "Asynchronous requests to URL: " + url);
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                HiLog.error(TAG, "Request failed: " + e.getLocalizedMessage());
                if (resultCallback != null) {
                    dispatcher.syncDispatch(() -> resultCallback.onFailed(request, e));
                } else {
                    HiLog.error(TAG, "resultCallback is null.");
                }
            }

            @Override
            public void onResponse(Call call, Response response) {
                String str;
                try {
                    str = Objects.requireNonNull(response.body()).string();
                    if (response.code() != 200) {
                        String exception;
                        JSONObject json = JSONObject.parseObject(str);
                        if (json == null) {
                            exception = str;
                        } else {
                            exception = json.getString("error");
                        }
                        String finalException = exception;

                        HiLog.info(TAG, "Request failed: " + finalException);
                        if (resultCallback != null) {
                            dispatcher.syncDispatch(() -> resultCallback.onFailed(request,
                                    new RuntimeException(finalException)));
                        } else {
                            HiLog.error(TAG, "resultCallback is null.");
                        }
                    } else {
                        final String finalStr = str;
                        if (resultCallback != null) {
                            dispatcher.syncDispatch(() -> resultCallback.onSuccess(finalStr));
                        } else {
                            HiLog.error(TAG, "resultCallback is null.");
                        }
                    }
                } catch (IOException e) {
                    HiLog.error(TAG, e.getLocalizedMessage());
                }
            }
        });
    }

    private static class Holder {
        private static final HttpHelper INSTANCE = new HttpHelper();
    }

    /**
     * 同步get请求
     *
     * @param url 网址
     * @return 结果
     */
    public Map<String,Object> synchronousGet(String url) {
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        return synchronousExecute(request);
    }

    /**
     * 异步get请求，不带参数
     *
     * @param url            网址
     * @param resultCallback 回调
     */
    public void asynchronousGet(String url, ResultCallback resultCallback) {
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        asynchronousExecute(request, resultCallback);
    }

    /**
     * 同步post请求，带json参数
     *
     * @param url  网址
     * @param json 参数
     * @return 结果
     */
    public Map<String,Object> synchronousPostWithJson(String url, JSONObject json) {
        Request request = new Request.Builder()
                .url(url)
                .post(RequestBody.create(MediaType.parse("application/json"), json.toString()))
                .build();
        return synchronousExecute(request);
    }

    /**
     * 异步post请求，带json参数
     *
     * @param url            网址
     * @param json           参数
     * @param resultCallback 回调
     */
    public void asynchronousPostWithJson(String url, JSONObject json, ResultCallback resultCallback) {
        Request request = new Request.Builder()
                .url(url)
                .post(RequestBody.create(MediaType.parse("application/json"), json.toString()))
                .build();
        asynchronousExecute(request, resultCallback);
    }

    /**
     * 同步delete请求，带json参数
     *
     * @param url  网址
     * @param json 参数
     * @return 结果
     */
    public Map<String,Object> synchronousDeleteWithJson(String url, JSONObject json) {
        Request request = new Request.Builder()
                .url(url)
                .delete(RequestBody.create(MediaType.parse("application/json"), json.toString()))
                .build();
        return synchronousExecute(request);
    }

    /**
     * 异步delete请求，带json参数
     *
     * @param url            网址
     * @param json           参数
     * @param resultCallback 回调
     */
    public void asynchronousDeleteWithJson(String url, JSONObject json, ResultCallback resultCallback) {
        Request request = new Request.Builder()
                .url(url)
                .post(RequestBody.create(MediaType.parse("application/json"), json.toString()))
                .build();
        asynchronousExecute(request, resultCallback);
    }

    /**
     * 两个回调方法
     */
    public static abstract class ResultCallback {
        /**
         * 失败回调
         *
         * @param request   请求
         * @param exception 错误信息
         */
        public abstract void onFailed(Request request, Exception exception);

        /**
         * 成功回调
         *
         * @param response 结果
         */
        public abstract void onSuccess(String response);
    }
}
